/*******************************************************************************
 * Copyright (c) 2014-3-14 @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>. All rights
 * reserved.
 *
 * Contributors: <a href="mailto:iffiff1@gmail.com">Tyler Chen</a> - initial API and implementation
 ******************************************************************************/
package org.iff.util;

/**
 * common exceptions.
 *
 * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
 * @since 2014-3-14
 */
public class Exceptions {
    private static String errorCode001 = "ERR-9001";
    private static String errorCode002 = "ERR-9002";

    public static void main(String[] args) {
        try {
            try {
                try {
                    throw new Exception("null");
                } catch (Exception e) {
                    Exceptions.runtime("entity", e, "ENT-001");
                }
            } catch (Exception e) {
                Exceptions.exception("dao", e, "DAO-001");
            }
        } catch (Exception e) {
            Exceptions.runtime("service", e, "SVC-001");
        }
    }

    static String format(CharSequence message, Object... args) {
        if (message == null || args == null || args.length == 0) {
            return message == null ? null : message.toString();
        }
        int argCount = 0, len = message.length(), argLen = args.length;
        StringBuilder sb = new StringBuilder(len * 2);
        for (int i = 0; i < len; i++) {
            char c = message.charAt(i);
            if (c == '{' && (i + 1) < len && message.charAt(i + 1) == '}') {
                sb.append(String.valueOf(argCount < argLen ? args[argCount] : ""));
                i = i + 1;
                argCount = argCount + 1;
                continue;
            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * throw Exception with Throwable error
     *
     * @param message   you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @param e
     * @param errorCode
     * @throws Exception
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void exception(CharSequence message, Throwable e, String errorCode, Object... args) throws Exception {
        throw new ErrorException(format(message, args), e, errorCode);
    }

    /**
     * throw Exception with Throwable error
     *
     * @param message   you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @param errorCode
     * @throws Exception
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void exception(CharSequence message, String errorCode, Object... args) throws Exception {
        throw new ErrorException(format(message, args), errorCode);
    }

    /**
     * throw Exception with Throwable error
     *
     * @param message you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @param e
     * @throws Exception
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void exception(CharSequence message, Throwable e, Object... args) throws Exception {
        throw new ErrorException(format(message, args), e);
    }

    /**
     * throw Exception without Throwable error
     *
     * @param message you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @throws Exception
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void exception(CharSequence message, Object... args) throws Exception {
        throw new ErrorException(format(message, args));
    }

    /**
     * throw RuntimeException with Throwable error
     *
     * @param message   you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @param e
     * @param errorCode
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void runtime(CharSequence message, Throwable e, String errorCode, Object... args) {
        throw new ErrorRuntimeException(format(message, args), e, errorCode);
    }

    /**
     * throw RuntimeException with Throwable error
     *
     * @param message   you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @param errorCode
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void runtime(CharSequence message, String errorCode, Object... args) {
        throw new ErrorRuntimeException(format(message, args), errorCode);
    }

    /**
     * throw RuntimeException with Throwable error
     *
     * @param message you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @param e
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void runtime(CharSequence message, Throwable e, Object... args) {
        throw new ErrorRuntimeException(format(message, args), e);
    }

    /**
     * throw RuntimeException without Throwable error
     *
     * @param message you can use com.foreveross.infra.util.FormatableCharSequence.get
     * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
     * @since 2014-3-14
     */
    public static void runtime(CharSequence message, Object... args) {
        throw new ErrorRuntimeException(format(message, args));
    }

    public static interface ErrorThrow {
        String getErrorCode();

        default Integer getErrorNumber() {
            String code = getErrorCode();
            if (code == null || code.length() < 1) {
                return 0;
            }
            String[] splits = code.split("-");
            code = splits[splits.length - 1];
            if (code == null || code.length() < 1) {
                return 0;
            }
            try {
                return Integer.valueOf(code);
            } catch (Exception e) {
            }
            return 0;
        }
    }

    @SuppressWarnings("serial")
    public static class ErrorException extends Exception implements ErrorThrow {

        private String errorCode = errorCode001;

        public ErrorException() {
            super();
        }

        public ErrorException(String message, Throwable cause) {
            super(message, cause, true, false);
        }

        public ErrorException(String message, String errorCode) {
            super(message);
            setErrorCode(errorCode == null ? errorCode001 : errorCode);
        }

        public ErrorException(String message, Throwable cause, String errorCode) {
            super(message, cause, true, false);
            setErrorCode(errorCode == null ? errorCode001 : errorCode);
        }

        public ErrorException(String message) {
            super(message);
        }

        public String getMessage() {
            return "[" + getErrorCode() + "] " + super.getMessage();
        }

        public String getErrorCode() {
            return this.errorCode;
        }

        public void setErrorCode(String errorCode) {
            this.errorCode = errorCode;
        }

        public String toString() {
            String s = "ErrorException";
            String message = getLocalizedMessage();
            return (message != null) ? (s + ": " + message) : s;
        }
    }

    @SuppressWarnings("serial")
    public static class ErrorRuntimeException extends RuntimeException implements ErrorThrow {
        /**
         * exception code.
         */
        private String errorCode = errorCode002;

        public ErrorRuntimeException() {
            super();
        }

        public ErrorRuntimeException(String message, Throwable cause) {
            super(message, cause, true, false);
        }

        public ErrorRuntimeException(String message, String errorCode) {
            super(message);
            setErrorCode(errorCode == null ? errorCode002 : errorCode);
        }

        public ErrorRuntimeException(String message, Throwable cause, String errorCode) {
            super(message, cause, true, false);
            setErrorCode(errorCode == null ? errorCode002 : errorCode);
        }

        public ErrorRuntimeException(String message) {
            super(message);
        }

        public String getMessage() {
            return "[" + getErrorCode() + "] " + super.getMessage();
        }

        public String getErrorCode() {
            return this.errorCode;
        }

        public void setErrorCode(String errorCode) {
            this.errorCode = errorCode;
        }

        public String toString() {
            String s = "ErrorRuntimeException";
            String message = getLocalizedMessage();
            return (message != null) ? (s + ": " + message) : s;
        }
    }
}
